package aupdg.logic.analysis.impl;

import aupdg.data.model.Ldg;
import aupdg.data.model.Node;
import aupdg.logic.analysis.InformationLeakAnalysis;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Non-interference analysis of a LDG.
 *
 * @author AA and CJ
 */
public class NonInterferenceAnalysis implements InformationLeakAnalysis {

    /**
     * LDG graph.
     */
    private Ldg graph;

    /**
     * Constructor, input parameter is the LDG. The graph should be labeled
     * previously with levels "High" and "Low". High -> private Low -> public
     *
     * @param graph
     */
    public NonInterferenceAnalysis(Ldg graph) {
        this.graph = graph;
    }

    /**
     * Check for non-interference properties in backward slice of a node. Return
     * a map with node and list(list of nodes) with the paths of the security
     * leaks found.
     *
     * @return
     */
    public Map<Node, List<List<Node>>> detectInformationLeaks() {
        HashMap<Node, List<List<Node>>> infoLeaksMap = new HashMap<>();

        Node slicingCNode;
        if (this.graph.getEntryNode() == null) {
            System.out.println("Err entry node not defined in ldg");
            return infoLeaksMap;
        }
        if (!areSecurityLevelsSet()) {
            System.out.println("Err security levels are not set");
            return infoLeaksMap;
        }
        System.out.println();

        for (int i = 0; i < this.graph.getNodeIds().size(); i++) {
            BackwardSlice fp = new BackwardSlice(graph);
            slicingCNode = this.graph.getNode(this.graph.getNodeIds().get(i));
            System.out.println("**********" + slicingCNode.getLabel() + "*************");
            fp.compute(this.graph.getEntryNode(), slicingCNode);
            System.out.println(fp.getBackwardSlice().toString());
            System.out.println(fp.getPathsWithNISecLevels());
            if (!fp.getPathsWithNISecLevels().isEmpty()) {
                infoLeaksMap.put(slicingCNode, fp.getPathsWithNISecLevels());
                printPathWithLeaks(fp.getPathsWithNISecLevels(), slicingCNode);
            }
        }
        return infoLeaksMap;
    }

    private void printPathWithLeaks(List<List<Node>> paths, Node slicingCNode) {
        for (int i = 0; i < paths.size(); i++) {
            System.out.println("Paths with leaks, slicing criterion node: " + slicingCNode.getLabel());
            for (int j = 0; j < paths.get(i).size(); j++) {
                System.out.print(paths.get(i).get(j).getLabel() + ",");

            }
        }
        System.out.println();
    }

    /**
     * Check if the security level is set for all nodes in LDG.
     *
     * @return boolean
     */
    private boolean areSecurityLevelsSet() {
        Node n;
        List<Integer> nodes = this.graph.getNodeIds();
        for (int i = 0; i < nodes.size(); i++) {
            n = this.graph.getNode(nodes.get(i));
            if (n.getLevel() == null) {
                return false;
            }
        }
        return true;
    }
//    public Map<String, List<List<String>>> infoLeaksToString(Map<Node, List<List<Node>>> map) {
//        HashMap stringMap = new HashMap();
//        for (Map.Entry<Node, List<List<Node>>> entry : map.entrySet()) {
//            //System.out.println(entry.getKey() + " " + entry.getValue());
//            stringMap.put(entry.getKey().getLabel(), entry.getValue());
//            List<String> strLeaks = new ArrayList<String>();
//            List<List<Node>> listNodes;
//            listNodes = entry.getValue();
//            for (int i = 0; i < listNodes.size(); i++) {
//            }
//
//        }
//        return stringMap;
//    }
}
